'''
NurbsCad
Archivo modules utilizado principalmente por el gui.py y escena.py
Autor: Gustavo Enrique Bellino
Contacto: gushbellino@gmail.com
Fecha: Junio de 2009
Descripcion: archivo que dispone de una clase que permite dibujar y manipular las propiedades de una NURBS
Licencia: GNU Public License version 2
'''

#Importa las librerias necesarias
import os, sys, math, numpy, copy
#Se agrega al path la carpeta ../lib
modulesdir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../lib'))
sys.path.insert(0, modulesdir)
#Se agrega al path la carpeta ../
modulesdir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))
sys.path.insert(0, modulesdir)
from lib.euclid import Vector3
from library import *

#Intenta cargar OpenGL, si no lo logra lanza un error y termina
try:
    from OpenGL.raw import GLU as simple
    from OpenGL import arrays
    from OpenGL.GL import GL_MAP2_VERTEX_3
    from OpenGL.GLU import gluNewNurbsRenderer
except:
    print 'ERROR: PyOpenGL is not installed properly.'
    sys.exit()

class NurbSurface(Library):
    ''' Clase que posibilita el renderizado de curvas y superficies NURBS en 3D. Dispone las acciones necesarias para agregar y editar 
    sus respectivas propiedades.'''
    def __init__(self, identifier, escena, parameters):
        '''Inicializa la clase de NURBS.
        @param: identifier, identificador que es un numero otorgado por la escena.
        @param: escena, objeto de la clase Escena que lo esta llamando (permite el uso de todas las funciones del padre)
        @param: parameters, parametros para crear el objeto.
        '''
        #parameters
        cantU = parameters[0]
        cantV = parameters[1]
        try:
            self.ordenU = parameters[2]
            self.ordenV = parameters[3]
        except:
            self.ordenU = 3
            self.ordenV = 3
        if(not(isinstance(cantU, int) and isinstance(cantV, int))):
            return False
        
        self.escena = escena
        
        #lista que guarda lista de puntos de control, para hacer ctrl+z
        self.undoOrden = []
        self.undoCtrl = []
        
        #identificador
        self.identifier = identifier
        self.autoEliminate = False #para eliminar el objeto
        self.visibleCurve = True
        self.visiblePoligon = True
        self.activeMover = False
        self.activeFill = False #modo de vista de la nurbs
        self.visiblePoints = True
        self.proposal = False
        
        #prueba
        self.cambiar = False #TODO
        self.lod = 1
        self.rotate = False
        self.fixedPoint = 0
        self.changeFixedPoint = False
        
        #prueba de vector nodal
        self.cv0 = 0
        self.cv1 = 1
        
        #color de la superficie
        import random
        red = random.random()
        green = random.random()
        blue = random.random()
        self.colorToDrawSurface= (red, green, blue, 0.0)
        self.colorToDrawPoints= (0.0, 1.0, 0.0, 0.0)
        self.colorToDrawPointFixed = (0, 0.5, 0.5, 0)
        self.colorToDrawLines= (1.0, 0.0, 0.0, 0.0)
        self.colorSelectedSurface = (0.0, 0.0, 1.0, 0.0)
        
        self.selectedCtrlPoint = 0
        
        self.iniciado = False
        self.cont = 0
        
        self.bSelected  = False
        self.cantU = cantU
        self.cantV = cantV
        self.cant = cantU * cantV
        self.cntrl = numpy.zeros((self.cantU, self.cantV,3), numpy.float)
        
        self.knotsU = [] #knots vectors
        self.knotsV = []
        
        #knot vector u
        knts = self.calculateKnots(self.cantU, self.ordenU)
        self.knotsU = knts[0]
        self.ordenU = knts[1]
        
        #knot vector u
        knts = self.calculateKnots(self.cantV, self.ordenV)
        self.knotsV = knts[0]
        self.ordenV = knts[1]
        
        self.ctrlPoints = []
        self.showPoints = True
        self.pointsText = False
        
        self.sampling_tolerance = 25.0
        self.heigth = 534 #SEE
        self.width = 678 #SEE
        
        self.type = 'nurb'
        self.name = 'nurb'+ str(identifier-1) #TODO nombre por parametro
        self.iniciateCtrlPoints()
        
        self.optimice = False
        self.success = True
        self.fambient = Vector3(0, 0, 0)
        return 

    def calculateKnots(self, cant, orden=3):
        #se define el orden
        if (orden>cant):
            orden = cant
        elif orden<2:
            orden = 2
        knots = [] #se inicializa el knot vector
        
        if cant < 4: #sera una curva de bezier
            for i in range(cant):
                knots.append(0)
            for i in range(cant):
                knots.append(1)
        else: #el tamano del orden es self.cantU + self.ordenU
            valor = range(int(cant-orden+2)) #los valores que puede tomar el knot vector
            tam = cant+orden
            cont = 0
#            print tam
            for i in range(int(tam)):
                if i < orden:
                    knots.append(valor[cont])
                elif i < (tam - orden):
                    cont += 1
                    knots.append(valor[cont])
                else:
                    knots.append(valor[cont+1])
#        print knots
        return (knots, orden)

    def getClassName():
        return 'NurbSurface'
    getClassName = staticmethod(getClassName)

    def getNameInMenu():
        return 'NURBS'
    getNameInMenu = staticmethod(getNameInMenu)

    def getParametersForCreate():
        title = 'Indique los parametros para crear una NURBS'
        parameters = [title, 
                      ('Cantidad U:', int), 
                      ('Cantidad V', int)
                      ]
        return parameters
    getParametersForCreate = staticmethod(getParametersForCreate)
    
    def iniciateCtrlPoints(self):
        self.ctrlPoints = []
        for u in range(self.cantU):
            for v in range(self.cantV):
                pto = Vector3(u + 1, v + 1, 0)
                self.ctrlPoints.append(pto)

    def changePoint(self, pos, punto):
        if(isinstance(punto, Vector3)):
            self.ctrlPoints[pos] = punto
        else:
            return None

    def getCtrlPoints(self):
        return self.ctrlPoints
    
    def setCtrlPoints(self, ctrlPoints):
        self.ctrlPoints = ctrlPoints
    
    def setSurfaceColor(self, color=(1.0, 1.0, 1.0, 0.5)):
        self.colorToDrawSurface = color

    def setPointsColor(self, color=(1.0, 1.0, 1.0, 0.5)):
        self.colorToDrawPoints= color
    
    def init_to_draw(self):
        cont = 0
        for u in range(self.cantU):
            for v in range(self.cantV):
                self.cntrl[u][v][0] = self.ctrlPoints[cont].x
                self.cntrl[u][v][1] = self.ctrlPoints[cont].y
                self.cntrl[u][v][2] = self.ctrlPoints[cont].z
                cont += 1
        #TODO
#        fambient=[0.2,0.09,0.03,0.5]#={0.2f,0.09f,0.03f,.5f},
#        fdiffuse=[self.fambient.x, self.fambient.y, self.fambient.z,0.5]#={0.2f,0.09f,0.03f,.5f},
#        fspecular=[0.6,0.2,0.07,0.5]#={0.6f,0.2f,0.07f,.5f},
#        bambient=[0.1,0.2,0.2,0.5]#={0.1f,0.2f,0.2f,0.5f},
#        bdiffuse=[0.5,0.9,0.6,.5]#={0.5f,0.9f,0.6f,.5f},
#        bspecular=[0.3,0.3,0.3,.5]#={0.3f,0.3f,0.3f,.5f};
#        fshininess=50;
#        bshininess=10;
#        glMaterialfv(GL_FRONT,GL_AMBIENT,fambient);
#        glMaterialfv(GL_FRONT,GL_DIFFUSE,fdiffuse);
#        glMaterialfv(GL_FRONT,GL_SPECULAR,fspecular);
#        glMateriali(GL_FRONT,GL_SHININESS,fshininess);
#        glMaterialfv(GL_BACK,GL_AMBIENT,bambient);
#        glMaterialfv(GL_BACK,GL_DIFFUSE,bdiffuse);
#        glMaterialfv(GL_BACK,GL_SPECULAR,bspecular);
#        glMateriali(GL_BACK,GL_SHININESS,bshininess);
        
        self.theNurb = gluNewNurbsRenderer()
        gluNurbsProperty(self.theNurb, GLU_SAMPLING_TOLERANCE, self.sampling_tolerance)
        
        if(self.activeFill):
            gluNurbsProperty(self.theNurb, GLU_DISPLAY_MODE, GLU_FILL)
        else:
            gluNurbsProperty(self.theNurb,GLU_DISPLAY_MODE,GLU_OUTLINE_POLYGON); #GLU_OUTLINE_POLYGON  GLU_FILL
    
    def draw_vertices(self, colorPoint = (0, 0, 0, 0),colorFixedPoint = (0, 0, 0, 0), colorLine = (0, 0, 0, 0)):
        glPushMatrix()
        cantPtos = self.ctrlPoints.__len__()
        radius = 0.08
        slices = 7
        stacks = 7
        color = colorPoint
        line_width = 2
        for i in range(cantPtos):
            origen = self.ctrlPoints[i]
            if(i==self.fixedPoint):
                color = colorFixedPoint
            else:
                color = colorPoint
            
            #Print the text of the point
            if(self.pointsText and self.selectedCtrlPoint==i):
#                (u, v) = self.get_u_v(i)
#                text = 'p('+str(u)+','+str(v)+')'+'='+self.printVector3(origen)+''
#                posWin = self.convertTo2D(origen.x, origen.y, origen.z)
#                self.drawText(text, int(posWin.x+3), int(self.heigth- posWin.y), self.heigth, self.width, 8, [0, 0, 0, 0])
#            elif(self.selectedCtrlPoint==i):
                (u, v) = self.get_u_v(i)
                text = 'p('+str(u)+','+str(v)+')'+'='+self.printVector3(origen)+''
                posWin = self.convertTo2D(origen.x, origen.y, origen.z)
                self.drawText(text, int(posWin.x+3), int(self.heigth- posWin.y), self.heigth, self.width, 8, [0, 0, 0, 0])
            if self.visiblePoints:
                self.draw_sphere(origen,  radius, slices, stacks, color)
            if(self.visiblePoligon):
                if((i+1)%self.cantV != 0 and i+1 < cantPtos ): #
                    self.draw_line(self.ctrlPoints[i], self.ctrlPoints[i+1], colorLine, line_width)
                if(i+self.cantV < cantPtos):
                    self.draw_line(self.ctrlPoints[i], self.ctrlPoints[i+self.cantV], colorLine, line_width)
        glPopMatrix()

    def draw(self, selectMode=False):
        if(not self.iniciado):
            self.init_to_draw()
        self.drawSurface(selectMode)

    def drawSurface(self, selectMode=False):
        ''' Dibuja la superficie en modo de seleccion o modo de renderizado'''
        #se setean los colores
        if(not selectMode):
            #modo de renderizado normal
            colorSurface = self.colorToDrawSurface
            colorPoints = self.colorToDrawPoints
            colorFixedPoint = self.colorToDrawPointFixed
            colorLine = self.colorToDrawLines
            if(self.bSelected):
                #admeas de no estar en modo seleccion esta seleccionado
                colorSurface = self.colorSelectedSurface
        else:
            #modo de seleccion, identifier representa a un color
            inc = (1.0/255)
            red = (inc) * self.identifier
            color = (red, red, red,  0)
            colorSurface  = color
            colorPoints = color
            colorFixedPoint = color
            colorLine = color
        
        self.draw_vertices(colorPoints, colorFixedPoint, colorLine)
        
        if(self.visibleCurve):
            if(self.cambiar): #TODO
                knotsU = [0,  0,  0,  0.25,  0.25,  0.25,  0.5,  0.5,  0.5,  0.75,  0.75,  0.75,  1, 1, 1, 1, 1, 1 ]
                knotsV = [0, 1]
            if(not self.optimice):
                glPushMatrix();
                glColor3f(colorSurface[0], colorSurface[1], colorSurface[2])                
                #aca se dibuja la superficie
                gluBeginSurface(self.theNurb);
                gluNurbsSurface(self.theNurb, 
                               self.knotsU, self.knotsV,
                               self.cntrl, 
                                GL_MAP2_VERTEX_3)
                gluEndSurface(self.theNurb)
                glPopMatrix();
                glFlush();
                self.iniciado = True

#CallBack Mouse
    def Mouse(self, x, y, camara, plano):
        #guarda los puntos de control
        self.saveToUndo()
        
        #Identifica si un punto de control fue seleccionado
        tolerance = 0.1
        pos = self.convertTo3D(x, y)
        recta = [camara.eyes, pos]
        
        #se busca el punto de control mas cercano al punto seleccionado
        cont = 0
        conj = []
        for p in self.getCtrlPoints():
            if(p.distFromRect(recta)<tolerance):
                conj.append((p, cont))
            cont += 1
        max = 100000
        
        for ptos in range(conj.__len__()):
            p = conj[ptos][0]
            dist = (camara.eyes - p).module()
            if dist < max:
                self.selectedCtrlPoint = conj[ptos][1] #indice del objeto
        if(not self.rotate and conj.__len__() !=0 ):
            plano.desplazar(self.getCtrlPoints()[self.selectedCtrlPoint])
            
        #cambiamos el punto fijo al punto seleccionado
        if(self.changeFixedPoint):
            self.fixedPoint = self.selectedCtrlPoint
            self.changeFixedPoint = False
            self.rotate = False
        elif(self.rotate): #rotan todos los puntos de control con punto fijo
            #se identifica el radio de la esfera
            self.origen = self.getCtrlPoints()[self.fixedPoint]
            pClicked = self.convertTo3D(x, y) #click en la pantalla
            recta = [camara.eyes, pClicked]
            self.v0 = self.getCtrlPoints()[self.selectedCtrlPoint]
            self.r = (self.v0 - self.origen).module()
            tolerance = 0.1
            if(self.r>tolerance):
                self.front = (camara.eyes-self.origen).module() >= (camara.eyes-self.v0).module()
                self.changed = False
            else:
                self.rotate = False
        self.escena.queue_draw()

    def Motion(self, x, y, width, heigth, camara, plano):
        ''' Motion de la NURBS '''
        #MOVER toda la NURBS
        self.iniciado = False
        if(self.activeMover and not self.rotate):
            #se edita todos los puntos seleccionados
            pos = self.convertTo3D(x, y)
            recta = [camara.eyes, pos]
            puntoNuevo = plano.interseccion(recta)
            
            #propone un punto nuevo
            if self.proposal:
                puntoNuevo = self.proponerPunto(puntoNuevo)
            
            plano.desplazar(puntoNuevo)
            desp = puntoNuevo - self.ctrlPoints[self.selectedCtrlPoint]
            
            #imprime el punto
            self.escena.set_info('Mover la NURBS activado: '+self.printVector3(puntoNuevo))
            for cont in range(self.ctrlPoints.__len__()):
                self.ctrlPoints[cont] = desp + self.ctrlPoints[cont]
        elif(not self.activeMover and not self.rotate):#EDITAR el punto de control seleccionado
            #se edita el punto seleccionado
            pos = self.convertTo3D(x, y)
            recta = [camara.eyes, pos]
            puntoNuevo = plano.interseccion(recta)
            
            #propone un punto
            if self.proposal:
                puntoNuevo = self.proponerPunto(puntoNuevo)
                
            plano.desplazar(puntoNuevo)
            self.changePoint(self.selectedCtrlPoint, puntoNuevo)
            
            #imprime el punto
            self.escena.set_info(self.printVector3(puntoNuevo))
        elif(self.rotate):#ROTAR todos los puntos de control
                pClicked = self.convertTo3D(x, y) #click en la pantalla
                p0 = camara.eyes
                p1 = pClicked
                c = self.origen
                r = self.r
                
                #los puntos que intersectan a la esfera virtual
                inter =  self.interEsfera(p0, p1, c, r) #el rayo subido a la esfera
                self.i = inter
                interceptaEsfera = inter.__len__()==2
                
                if(interceptaEsfera): #intercepta a la esfera
                    self.changed = False #cambiado el front
                    if(self.front): # si esta en el frente o atras
                        v1 = inter[0]
                    else:
                        v1 = inter[1]
                else: #esta fuera de la esfera
                    v1 = inter[0] #el costado de la esfera
                    if(not self.changed):
                        self.front = not self.front
                        self.changed = True
                
                #propone un punto
                if self.proposal:
                    v1 = self.proponerPunto(v1)
                
                #imprime el punto
                self.escena.set_info(self.printVector3(v1))
                
                #rota el objeto
                eje = (v1-self.origen).cross(self.v0-self.origen)
                self.eje = eje
                ang = (v1-self.origen).angle(self.v0-self.origen)
                
                cont = 0
                for p in self.getCtrlPoints():
                    if(cont!=self.fixedPoint):
                        temp = self.getCtrlPoints()[cont] - self.origen
                        temp = temp.rotate(-ang, self.eje)
                        self.getCtrlPoints()[cont] = temp + self.origen
                    cont += 1
                #actualiza el punto
                self.v0 = v1                        
                return 0
        return 0

    def proponerPunto(self, pto, tol = 0.2):
        puntoNuevo = pto
        for indice in self.escena.objects.keys():
            if(isinstance(self.escena.objects[indice], NurbSurface)):
                if not indice == self.identifier: #si no es la misma nurbs
                    nurb = self.escena.objects[indice]
                    cantV = nurb.cantV
                    cantU = nurb.cantU
                    ctrlPoints = nurb.ctrlPoints
                    for i in range(cantV*cantU):
                        tmp = ctrlPoints[i] - puntoNuevo
                        if tmp.module() < tol:
                            return ctrlPoints[i]
                else: #si es la misma nurbs pregunta que no sea el punto seleccionado
                    nurb = self.escena.objects[indice]
                    cantV = nurb.cantV
                    cantU = nurb.cantU
                    ctrlPoints = nurb.ctrlPoints
                    for i in range(cantV*cantU):
                        if not i == self.selectedCtrlPoint: #que no sea el punto seleccionado
                            tmp = ctrlPoints[i] - puntoNuevo
                            if tmp.module() < tol:
                                return ctrlPoints[i]
        return puntoNuevo

    def Resize(self, newWidth, newHeigth):
        self.width = newWidth
        self.heigth = newHeigth
        return

    def Scroll(self, plano, direction):
        self.iniciado = False
        plano.Scroll(direction)

#retorna los puntos que interceptan la esfera, si no la intercepta retorna falso
    def interEsfera(self, p0, p1, c, r):
        v = (p1 - p0)
        v.normalize()
        cP = (c-p0).dot(v) #c paralelo
        cP = cP * v
        cT = (c-p0)-cP

        modulo = cT.module()
        if(modulo<=r):
            R1 = math.sqrt(r**2 - modulo**2)
            p_back = p0 + cP + v*R1
            p_front = p0 + cP - v*R1
            return [p_front, p_back]
        else:
            return [-cT + c]

    def get_u_v(self, indice):
        v = indice % self.cantV
        u = indice / self.cantV
        return (u, v)

    def getSP(self):
        return self.sampling_tolerance

    def getGradoU(self):
        return self.ordenU-1
    
    def setGradoU(self, spinbutton_widget1):
        valor = spinbutton_widget1.get_value()
        redond = round(valor)
        dif = valor - redond
        if abs(dif) < 0.001:
            if valor + 1 > self.cantU or valor + 1 < 2 or dif>0.01:
                spinbutton_widget1.set_value(self.ordenU-1)
                return
            self.ordenU = valor +1
            knts = self.calculateKnots(self.cantU, self.ordenU)
            self.knotsU = knts[0]
            self.ordenU = knts[1]
            self.iniciado = False
        else:
            if dif > 0:
                self.ordenU += 1
            else:
                self.ordenU -= 1
#            print 'ordenU', self.ordenU
            spinbutton_widget1.set_value(self.ordenU-1)
            knts = self.calculateKnots(self.cantU, int(self.ordenU))
            self.knotsU = knts[0]
            self.ordenU = knts[1]
            self.iniciado = False
        self.saveToUndo()
        self.escena.queue_draw()
        return 

    def getGradoV(self):
        return self.ordenV-1

    def setGradoV(self, spinbutton_widget1):
        valor = spinbutton_widget1.get_value()
        redond = round(valor)
        dif = valor - redond
        if abs(dif) < 0.001:
            if valor + 1 > self.cantV or valor + 1 < 2 or dif>0.01:
                spinbutton_widget1.set_value(self.ordenV-1)
                return
            self.ordenV = valor +1
            knts = self.calculateKnots(self.cantV, int(self.ordenV))
            self.knotsV = knts[0]
            self.ordenV = knts[1]
            self.iniciado = False
        else:
            if dif > 0:
                self.ordenV += 1
            else:
                self.ordenV -= 1
            spinbutton_widget1.set_value(self.ordenV-1)
            knts = self.calculateKnots(self.cantV, self.ordenV)
            self.knotsV = knts[0]
            self.ordenV = knts[1]
            self.iniciado = False
        self.saveToUndo()
        self.escena.queue_draw()
        return 

    def atributesToEdit(self):
        self.atributes = {'SP': (self.getSP(), lambda widget:self.setSampTol(widget)), 
                                'Grado U': (self.getGradoU(), lambda widget:self.setGradoU(widget)), 
                                'Grado V': (self.getGradoV(), lambda widget:self.setGradoV(widget)), 
                                'fambient': (self.getfambient(), lambda widget,spinner1,spinner2,spinner3:self.setfambient(widget,spinner1,spinner2,spinner3))}
        #Puntos de control
        for cont in range(self.ctrlPoints.__len__()):
            (u, v) = self.get_u_v(cont)
            stringToShow = 'Pto(' + str(u) + ',' + str(v) +')'
            self.atributes[stringToShow] = (self.ctrlPoints[cont], lambda widget,spinner1,spinner2,spinner3, cont:  self.setCntrlPoint(widget,spinner1,spinner2,spinner3,cont), cont)
        #knot vector U
        for cont in range(self.knotsU.__len__()):
            if cont<10:
                stringToShow = 'knotU(0' + str(cont)  +')'
            else:
                stringToShow = 'knotU(' + str(cont)  +')'
            self.atributes[stringToShow] = (self.knotsU[cont], lambda widget, cont:  self.setKnotU(widget,cont), cont)
        #knot vector V
        for cont in range(self.knotsV.__len__()):
            stringToShow = 'knotV(' + str(cont)  +')'
            self.atributes[stringToShow] = (self.knotsV[cont], lambda widget, cont:  self.setKnotV(widget,cont), cont)
        return self.atributes

    def setKnotU(self, spinbutton_widget1, indice_knot):
        valor = spinbutton_widget1.get_value()  
        self.knotsU[indice_knot] = valor
        self.iniciado = False
        self.escena.queue_draw()
        return True

    def setKnotV(self, spinbutton_widget1, indice_knot):
        valor = spinbutton_widget1.get_value()  
        self.knotsV[indice_knot] = valor
        self.iniciado = False
        self.escena.queue_draw()
        return True

    def getfambient(self):
        return self.fambient

    def setfambient(self, widget, spinbutton_widget1,spinbutton_widget2,spinbutton_widget3):
        valorX = spinbutton_widget1.get_value()
        valorY = spinbutton_widget2.get_value()
        valorZ = spinbutton_widget3.get_value()
        valorV=Vector3(valorX,valorY,valorZ);      
        self.fambient = valorV
        return True

    def setCntrlPoint(self,widget, spinbutton_widget1,spinbutton_widget2,spinbutton_widget3,indice_punto):
        valorX = spinbutton_widget1.get_value()
        valorY = spinbutton_widget2.get_value()
        valorZ = spinbutton_widget3.get_value()
        valorV=Vector3(valorX,valorY,valorZ);      
        self.ctrlPoints[indice_punto] = valorV
        self.iniciado = False
        self.escena.queue_draw()
        return True

    def setLod(self, widget):
        valor = widget.get_value()
        try:
            exec('v = ' + valor)
        except:
            return False
        self.lod = v

    def setCV0(self, widget):
        #controlar si es un sampling tolerance correcto
        valor = widget.get_value()
        try:
            exec('v = ' + valor)
        except:
            return False
        self.cv0 = v
        return True

    def setCV1(self, widget):
        #controlar si es un sampling tolerance correcto
        valor = widget.get_value()
        try:
            exec('v = ' + valor)
        except:
            return False
        self.cv1 = v
        return True

    def setSampTol(self, spinbutton_widget1):
        #controlar si es un sampling tolerance correcto
        valor = spinbutton_widget1.get_value()
        self.sampling_tolerance = round(valor)
        return True

    def setCantU(self, u):
        self.cantU = u

    def setCantV(self, v):
        self.cantV = v

    def actionsToDo(self):
        
        move = 'move.png'
        moveToolTip = 'Mover la NURBS: Activar'
        if(self.activeMover):
            move = 'move_selected.png'
            moveToolTip = 'Mover la NURBS: Desactivar'
        
        visibleCurve = 'seeCurve.png'
        visibleCurveToolTip = 'Visualizar la NURBS'
        if(self.visibleCurve):
            visibleCurve = 'hideCurve.png'
            visibleCurveToolTip = 'Ocultar la NURBS'
        
        visiblePoligon = 'seePoligon.png'
        visiblePoligonToolTip = 'Visualizar Poligono de control'
        if(self.visiblePoligon):
            visiblePoligon = 'hidePoligon.png'
            visiblePoligonToolTip = 'Ocultar Poligono de control'
        
        visibleAll = 'seeAll.png'
        visibleAllBool = True
        visibleAllToolTip = 'Visualizar Todo'
        if(self.visibleCurve and self.visiblePoligon and self.visiblePoints):
            visibleAll = 'hideAll.png'
            visibleAllBool = False
            visibleAllToolTip = 'Ocultar Todo'
            
        visibleFilled = 'filled.png'
        visibleFilledToolTip = 'Colocar relleno de la NURBS'
        if(self.activeFill):
            visibleFilled = 'notFilled.png'
            visibleFilledToolTip = 'Retirar relleno de la NURBS'
         
        rotate = 'rotate.png'
        rotateToolTip = 'Rotar NURBS: Activar'
        if(self.rotate):
            rotate = 'rotate_selected.png'
            rotateToolTip = 'Rotar NURBS: Desactivar'           
            
        setPf = 'fixPoint.png'
        setPfToolTip = 'Setear un nuevo punto fijo'
        if self.changeFixedPoint:
            setPf = 'fixPoint_selected.png'
            setPfToolTip = 'Setear un nuevo punto fijo'
        
        visibleText = 'seeTexts.png'
        visibleTextToolTip = 'Mostrar coordenadas de los puntos de control'
        if self.pointsText:
            visibleText = 'hideTexts.png'
            visibleTextToolTip = 'Ocultar coordenadas de los puntos de control'
        
        visiblePoints = 'seePoints.png'
        visiblePointsToolTip = 'Mostrar los puntos de control'
        if self.visiblePoints:
            visiblePoints = 'hidePoints.png'
            visiblePointsToolTip = 'Ocultar los puntos de control'
        
        pntFijo = 'Cambiar Punto Fijo ('+str(self.get_u_v(self.fixedPoint)) + ')'
        
        self.actions = {
                            '01-nurbsIcons/upp.png': (lambda x: self.addU(), 'Agregar ptos de control en la direccion "u"'), 
                            '02-nurbsIcons/vpp.png': (lambda x: self.addV(), 'Agregar ptos de control en la direccion "v"'), 
                            '03-nurbsIcons/'+move:    (lambda x: self.setMover(), moveToolTip), 
                            '04-nurbsIcons/'+visibleCurve:    (lambda x: self.setVisibleCurve(), visibleCurveToolTip), 
                            '05-nurbsIcons/'+visiblePoligon:    (lambda x: self.setVisiblePoligon(), visiblePoligonToolTip), 
                            '06-nurbsIcons/'+visiblePoints:(lambda x:self.setVisiblePoints(), visiblePointsToolTip), 
                            '07-nurbsIcons/'+visibleAll: (lambda x:self.setVisibleNurbs(visibleAllBool), visibleAllToolTip), 
                            '08-nurbsIcons/eliminate.png':    (lambda x: self.eliminarse(), 'Eliminar NURBS'), 
                            '09-nurbsIcons/'+visibleFilled: (lambda x:self.setFilled(), visibleFilledToolTip), 
                            '10-nurbsIcons/'+rotate: (lambda x: self.setRotate(), rotateToolTip), 
                            '11-nurbsIcons/'+setPf: (lambda x: self.setFixedPoint(), setPfToolTip), 
                            '12-nurbsIcons/'+visibleText:(lambda x:self.showPointText(), visibleTextToolTip), 
                            '13-nurbsIcons/grado_u_pp.png':(lambda x:self.addOrdenU(1), 'Aumentar el grado en u'), 
                            '14-nurbsIcons/grado_u_LL.png':(lambda x:self.addOrdenU(-1), 'Disminuir el grado en u'), 
                            '15-nurbsIcons/grado_v_pp.png':(lambda x:self.addOrdenV(1), 'Aumentar el grado en v'), 
                            '16-nurbsIcons/grado_v_LL.png':(lambda x:self.addOrdenV(-1), 'Disminuir el grado en v'), 
                            '17-nurbsIcons/undo.png':(lambda x:self.undoAction(), 'Undo'), 
                            '18-nurbsIcons/espejarY.png':(lambda x:self.espejar('y'), 'Espejar sobre el plano x-z'), 
                            '19-nurbsIcons/espejarX.png':(lambda x:self.espejar('x'), 'Espejar sobre el plano y-z'), 
                            '20-nurbsIcons/espejarZ.png':(lambda x:self.espejar('z'), 'Espejar sobre el plano x-y'), 
                            '21-nurbsIcons/copy.png':(lambda x:self.copy(), 'copy'), 
                            '22-nurbsIcons/paste.png':(lambda x:self.paste(), 'paste'), 
                            '23-nurbsIcons/proponer.png':(lambda x:self.proponerActivar(), 'Encagar puntos.')
                            
                        }
        #'24-nurbsIcons/todo.png':(lambda x:self.cambiarKnots(), 'Generar Circulo.')
        return self.actions.keys()
    
    def proponerActivar(self):
        self.proposal = not self.proposal
        self.escena.proponerActivar(self.proposal)
        if self.proposal:
            self.escena.set_info('Encagar Puntos: activado.')
        else:
            self.escena.set_info('Encagar Puntos: desactivado.')
        
    
    def addOrdenU(self, inc):
        self.ordenU += inc
        knts = self.calculateKnots(self.cantU, self.ordenU)
        self.knotsU = knts[0]
        self.ordenU = knts[1]
        self.iniciado = False
        self.saveToUndo()
        self.escena.queue_draw()
        return
    
    def addOrdenV(self, inc):
        self.ordenV += inc
        knts = self.calculateKnots(self.cantV, self.ordenV)
        self.knotsV = knts[0]
        self.ordenV = knts[1]
        self.iniciado = False
        self.saveToUndo()
        self.escena.queue_draw()
        return
    
    def showPointText(self):
        self.pointsText = not self.pointsText
        self.escena.queue_draw()
    
    def setFixedPoint(self):
        self.changeFixedPoint = not self.changeFixedPoint
        self.escena.queue_draw()
    
    def setRotate(self):
        self.rotate = not self.rotate
        self.escena.queue_draw()
    
    def invertir(self):
        self.ctrlPoints.reverse()
        self.escena.queue_draw()
    
    def cambiarKnots(self): #TODO
        self.cantU  = 9
        self.cantV = 1
        self.ctrlPoints = [Vector3(1.0,0.0,0.0),
                           Vector3(1,1,0.0),
                           Vector3(0,1,0.0),
                           Vector3(-1.0,1.0,0.0),
                           Vector3(-1.0,0.0,0.0),
                           Vector3(-1.0,-1.0,0.0),
                           Vector3(0.0,-1.0,0.0),
                           Vector3(1.0,-1.0,0.0),
                           Vector3(1.0,0.0,0.0)]
        self.cntrl = numpy.zeros((self.cantU, self.cantV,3), numpy.float)
        
        #se modifica el knot vector
        knts = self.calculateKnots(self.cantU, self.ordenU)
        self.knotsU = knts[0]
        self.ordenU = knts[1]
        
        knts = self.calculateKnots(self.cantV, self.ordenV)
        self.knotsV = knts[0]
        self.ordenV = knts[1]
        print self.knotsU
        print self.ordenU
##        knotsU = [0,  0,  0,  0.25,  0.25,  0.25,  0.5,  0.5,  0.5,  0.75,  0.75,  0.75,  1, 1, 1, 1, 1, 1 ]
##        knotsV = [0, 1]
#        self.knotsU = [0,  0,  0,  0.25,  0.25,  0.25,  0.5,  0.5,  0.5,  0.75,  0.75,  0.75,  1, 1, 1, 1, 1, 1 ]
#        self.ordenU = [0, 1]
        self.iniciado = False
        
        self.cambiar = not self.cambiar
        self.iniciado = False
        self.escena.queue_draw()
    
    def setVisibleCurve(self):
        self.visibleCurve = not self.visibleCurve
        self.escena.queue_draw()
        if self.visibleCurve:
            self.escena.set_info('Curva visible.')
        else:
            self.escena.set_info('Curva invisible.')

    def setVisiblePoligon(self):
        self.visiblePoligon = not self.visiblePoligon
        self.escena.queue_draw()
        if self.visiblePoligon:
            self.escena.set_info('Poligono visible.')
        else:
            self.escena.set_info('Poligono invisible.')

    def setVisibleNurbs(self, bool):
        self.visiblePoligon = bool
        self.visibleCurve = bool
        self.visiblePoints = bool
        if bool:
            self.escena.set_info('Todos las propiedades visibles.')
        else:
            self.escena.set_info('Todos las propiedades invisibles.')
        self.escena.queue_draw()

    def setVisiblePoints(self):
        self.visiblePoints = not self.visiblePoints
        self.escena.queue_draw()
        if self.visiblePoints:
            self.escena.set_info('Puntos de control visibles.')
        else:
            self.escena.set_info('Puntos de control invisibles.')

    def setMover(self):
        self.rotate = False
        self.activeMover = not self.activeMover
        self.escena.queue_draw()
        self.escena.set_info('Arrastre un punto de control para mover la NURBS.')

    def eliminarse(self):
        self.autoEliminate = True
        self.escena.queue_draw()
        self.escena.set_info('El objeto se ha eliminado.')
        return

    def setFilled(self):
        self.activeFill = not self.activeFill
        self.iniciado = False
        self.escena.queue_draw()
        if self.activeFill:
            self.escena.set_info('Rellenado activado.')
        else:
            self.escena.set_info('Rellenado desactivado.')

    def addU(self):
        if(self.cantU==1):
            for v in range(self.cantV):
                pto = self.ctrlPoints[v]
                pto = Vector3(pto.x + 1, pto.y, pto.z)
                self.ctrlPoints.append(pto)
        elif(self.cantU>1):
            for v in range(self.cantV):
                pto1 = self.ctrlPoints[v+self.cantV*(self.cantU-1)]
                pto2 = self.ctrlPoints[v+self.cantV*(self.cantU-2)]
                pto = pto1 - pto2
                pto = pto.normalize() + pto1
                pto = pto
                self.ctrlPoints.append(pto)
            
        self.cantU += 1
        self.cntrl = numpy.zeros((self.cantU, self.cantV,3), numpy.float)
        self.escena.set_info('Existen '+str(self.cantU) + ' nodos en la direccion u.')
        
        #se modifica el knot vector
        knts = self.calculateKnots(self.cantU, self.ordenU)
        self.knotsU = knts[0]
        self.ordenU = knts[1]
        self.iniciado = False
        self.saveToUndo()
        self.escena.queue_draw()
        return 
    
    def addV(self):
        temp = []
        if self.cantV == 1:
            differentZ = False
            for u in range(self.cantU):
                z = self.ctrlPoints[u].z
                for u in range(self.cantU):
                    if self.ctrlPoints[u].z - z > 0.3:
                        differentZ = True
                        break
        
        for u in range(self.cantU):
            for v in range(self.cantV):
                temp.append(self.ctrlPoints[v+(self.cantV*u)])
            if(self.cantV==1):
                pto = self.ctrlPoints[u]
                if differentZ:
                    pto = Vector3(pto.x, pto.y + 1, pto.z)
                else:
                    pto = Vector3(pto.x, pto.y , pto.z + 1)
            elif(self.cantV>1):
                pto1 = self.ctrlPoints[(self.cantV-1)+(self.cantV*u)]
                pto2 = self.ctrlPoints[(self.cantV-2)+(self.cantV*u)]
                pto = pto1 - pto2
                pto = pto.normalize() + pto1
            temp.append(pto)
        
        self.ctrlPoints = temp
        self.cantV += 1
        self.cntrl = numpy.zeros((self.cantU, self.cantV,3), numpy.float)
        self.escena.set_info('Existen '+str(self.cantV) + ' nodos en la direccion v.')
        
        #se modifica el knot vector
        knts = self.calculateKnots(self.cantV, self.ordenV)
        self.knotsV = knts[0]
        self.ordenV = knts[1]
        self.iniciado = False
        self.escena.queue_draw()
        return 

    def saveToUndo(self):
        #cant U
        cantU = copy.copy(self.cantU)
        #cant V
        cantV = copy.copy(self.cantV)
        #puntos de control
        ctrlPoints = copy.copy(self.ctrlPoints)
        #orden U 
        ordenU = copy.copy(self.ordenU)
        #orden V
        ordenV = copy.copy(self.ordenV)
        #knot U
        knotU = copy.copy(self.knotsU)
        #knot V
        knotV = copy.copy(self.knotsV)
        self.undoCtrl.append([ordenU, ordenV, ctrlPoints,  knotU, knotV, cantU, cantV])

    def undoAction(self):
        #puntos de control
        tam = self.undoCtrl.__len__()
        if tam!=0:
            undo = self.undoCtrl.pop(tam-1)
            #puntos de control
            ctrlPoints = undo[2]
            #orden U 
            ordenU = undo[0]
            #orden V
            ordenV = undo[1]
            #knot U
            knotU = undo[3]
            #knot V
            knotV = undo[4]
            #cant U
            cantU = undo[5]
            #cant V
            cantV = undo[6]
            self.iniciado = False
            self.ctrlPoints = ctrlPoints
            self.ordenU = ordenU
            self.ordenV = ordenV
            if (self.cantU != cantU) or (self.cantV != cantV):
                self.cntrl = numpy.zeros((cantU, cantV,3), numpy.float)
            self.cantU = cantU
            self.cantV = cantV
            knts = self.calculateKnots(self.cantU, self.ordenU)
            self.knotsU = knts[0]
            knts = self.calculateKnots(self.cantV, self.ordenV)
            self.knotsV = knts[0]
            self.iniciado = False
            self.escena.queue_draw()
        self.escena.set_info('La accion ha sido revertida.')

    def espejar(self, eje):
        #desplazar al eje cero
        ini = self.ctrlPoints[0]
        for i in range(self.cantV*self.cantU):
            self.ctrlPoints[i] = self.ctrlPoints[i] - ini
        
        #espeja con respecto al eje z
        if eje == 'x':
            for i in range(self.cantV*self.cantU):
                self.ctrlPoints[i].x = -self.ctrlPoints[i].x
            plano = 'y-z'
        if eje == 'y':
            for i in range(self.cantV*self.cantU):
                self.ctrlPoints[i].y = -self.ctrlPoints[i].y
            plano = 'x-z'
        if eje == 'z':
            for i in range(self.cantV*self.cantU):
                self.ctrlPoints[i].z = -self.ctrlPoints[i].z
            plano = 'x-y'
        #desplazar nuevamente
        for i in range(self.cantV*self.cantU):
            self.ctrlPoints[i] = self.ctrlPoints[i] + ini
            
        
        self.escena.set_info('La accion de espejado sobre el plano ' +plano + ' completada.')
        self.iniciado = False
        self.escena.queue_draw()
        return 

    def copy(self):
        objeto = copy.copy(self)
        self.escena.copy(objeto)

    def paste(self):
        self.escena.paste()
        self.escena.queue_draw()

